home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 001-025 / disk_023 / ver30 / line.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  15KB  |  583 lines

  1. /*
  2.  * Name:    MicroEMACS
  3.  *        Text line handling.
  4.  * Version:    29
  5.  * Last edit:    14-Feb-86
  6.  * By:        rex::conroy, vox::ellison
  7.  *        decvax!decwrl!dec-rhea!dec-rex!conroy
  8.  *                   ...!dec-vox!ellison
  9.  *
  10.  * The functions in this file
  11.  * are a general set of line management
  12.  * utilities. They are the only routines that
  13.  * touch the text. They also touch the buffer
  14.  * and window structures, to make sure that the
  15.  * necessary updating gets done. There are routines
  16.  * in this file that handle the kill buffer too.
  17.  * It isn't here for any good reason.
  18.  *
  19.  * Note that this code only updates the dot and
  20.  * mark values in the window list. Since all the code
  21.  * acts on the current window, the buffer that we
  22.  * are editing must be being displayed, which means
  23.  * that "b_nwnd" is non zero, which means that the
  24.  * dot and mark values in the buffer headers are
  25.  * nonsense.
  26.  */
  27. #include    "def.h"
  28.  
  29. #define    NBLOCK    16            /* Line block chunk size    */
  30.  
  31. #ifndef    KBLOCK
  32. #define    KBLOCK    256            /* Kill buffer block size.    */
  33. #endif
  34.  
  35. char    *kbufp    = NULL;            /* Kill buffer data.        */
  36. int    kused    = 0;            /* # of bytes used in KB.    */
  37. int    ksize    = 0;            /* # of bytes allocated in KB.    */
  38.  
  39. /*
  40.  * This routine allocates a block
  41.  * of memory large enough to hold a LINE
  42.  * containing "used" characters. The block is
  43.  * always rounded up a bit. Return a pointer
  44.  * to the new block, or NULL if there isn't
  45.  * any memory left. Print a message in the
  46.  * message line if no space.
  47.  */
  48. LINE    *
  49. lalloc(used)
  50. register int    used;
  51. {
  52.     register LINE    *lp;
  53.     register int    size;
  54.  
  55.     size = (used+NBLOCK-1) & ~(NBLOCK-1);
  56.     if (size == 0)                /* Assume that an empty    */
  57.         size = NBLOCK;            /* line is for type-in.    */
  58.     if ((lp=(LINE *)malloc(sizeof(LINE)+size)) == NULL) {
  59.         eprintf("Cannot allocate %d bytes", size);
  60.         return (NULL);
  61.     }
  62.     lp->l_size = size;
  63.     lp->l_used = used;
  64.     return (lp);
  65. }
  66.  
  67. /*
  68.  * Delete line "lp". Fix all of the
  69.  * links that might point at it (they are
  70.  * moved to offset 0 of the next line.
  71.  * Unlink the line from whatever buffer it
  72.  * might be in. Release the memory. The
  73.  * buffers are updated too; the magic conditions
  74.  * described in the above comments don't hold
  75.  * here.
  76.  */
  77. lfree(lp)
  78. register LINE    *lp;
  79. {
  80.     register BUFFER    *bp;
  81.     register WINDOW    *wp;
  82.  
  83.     wp = wheadp;
  84.     while (wp != NULL) {
  85.         if (wp->w_linep == lp)
  86.             wp->w_linep = lp->l_fp;
  87.         if (wp->w_dotp  == lp) {
  88.             wp->w_dotp  = lp->l_fp;
  89.             wp->w_doto  = 0;
  90.         }
  91.         if (wp->w_markp == lp) {
  92.             wp->w_markp = lp->l_fp;
  93.             wp->w_marko = 0;
  94.         }
  95.         wp = wp->w_wndp;
  96.     }
  97.     bp = bheadp;
  98.     while (bp != NULL) {
  99.         if (bp->b_nwnd == 0) {
  100.             if (bp->b_dotp  == lp) {
  101.                 bp->b_dotp = lp->l_fp;
  102.                 bp->b_doto = 0;
  103.             }
  104.             if (bp->b_markp == lp) {
  105.                 bp->b_markp = lp->l_fp;
  106.                 bp->b_marko = 0;
  107.             }
  108.         }
  109.         bp = bp->b_bufp;
  110.     }
  111.     lp->l_bp->l_fp = lp->l_fp;
  112.     lp->l_fp->l_bp = lp->l_bp;
  113.     free((char *) lp);
  114. }
  115.  
  116. /*
  117.  * This routine gets called when
  118.  * a character is changed in place in the
  119.  * current buffer. It updates all of the required
  120.  * flags in the buffer and window system. The flag
  121.  * used is passed as an argument; if the buffer is being
  122.  * displayed in more than 1 window we change EDIT to
  123.  * HARD. Set MODE if the mode line needs to be
  124.  * updated (the "*" has to be set).
  125.  */
  126. lchange(flag)
  127. register int    flag;
  128. {
  129.     register WINDOW    *wp;
  130.  
  131.     if (curbp->b_nwnd != 1)            /* Ensure hard.        */
  132.         flag = WFHARD;
  133.     if ((curbp->b_flag&BFCHG) == 0) {    /* First change, so     */
  134.         flag |= WFMODE;            /* update mode lines.    */
  135.         curbp->b_flag |= BFCHG;
  136.     }
  137.     wp = wheadp;
  138.     while (wp != NULL) {
  139.         if (wp->w_bufp == curbp)
  140.             wp->w_flag |= flag;
  141.         wp = wp->w_wndp;
  142.     }
  143. }
  144.  
  145. /*
  146.  * Insert "n" copies of the character "c"
  147.  * at the current location of dot. In the easy case
  148.  * all that happens is the text is stored in the line.
  149.  * In the hard case, the line has to be reallocated.
  150.  * When the window list is updated, take special
  151.  * care; I screwed it up once. You always update dot
  152.  * in the current window. You update mark, and a
  153.  * dot in another window, if it is greater than
  154.  * the place where you did the insert. Return TRUE
  155.  * if all is well, and FALSE on errors.
  156.  */
  157. linsert(n, c)
  158. {
  159.     register char    *cp1;
  160.     register char    *cp2;
  161.     register LINE    *lp1;
  162.     register LINE    *lp2;
  163.     register LINE    *lp3;
  164.     register int    doto;
  165.     register int    i;
  166.     register WINDOW    *wp;
  167.  
  168.     lchange(WFEDIT);
  169.     lp1 = curwp->w_dotp;            /* Current line        */
  170.     if (lp1 == curbp->b_linep) {        /* At the end: special    */
  171.         if (curwp->w_doto != 0) {
  172.             eprintf("bug: linsert");
  173.             return (FALSE);
  174.         }
  175.         if ((lp2=lalloc(n)) == NULL)    /* Allocate new line    */
  176.             return (FALSE);
  177.         lp3 = lp1->l_bp;        /* Previous line    */
  178.         lp3->l_fp = lp2;        /* Link in        */
  179.         lp2->l_fp = lp1;
  180.         lp1->l_bp = lp2;
  181.         lp2->l_bp = lp3;
  182.         for (i=0; i<n; ++i)
  183.             lp2->l_text[i] = c;
  184.         curwp->w_dotp = lp2;
  185.         curwp->w_doto = n;
  186.         return (TRUE);
  187.     }
  188.     doto = curwp->w_doto;            /* Save for later.    */
  189.     if (lp1->l_used+n > lp1->l_size) {    /* Hard: reallocate    */
  190.         if ((lp2=lalloc(lp1->l_used+n)) == NULL)
  191.             return (FALSE);
  192.         cp1 = &lp1->l_text[0];
  193.         cp2 = &lp2->l_text[0];
  194.         while (cp1 != &lp1->l_text[doto])
  195.             *cp2++ = *cp1++;
  196.         cp2 += n;
  197.         while (cp1 != &lp1->l_text[lp1->l_used])
  198.             *cp2++ = *cp1++;
  199.         lp1->l_bp->l_fp = lp2;
  200.         lp2->l_fp = lp1->l_fp;
  201.         lp1->l_fp->l_bp = lp2;
  202.         lp2->l_bp = lp1->l_bp;
  203.         free((char *) lp1);
  204.     } else {                /* Easy: in place    */
  205.         lp2 = lp1;            /* Pretend new line    */
  206.         lp2->l_used += n;
  207.         cp2 = &lp1->l_text[lp1->l_used];
  208.         cp1 = cp2-n;
  209.         while (cp1 != &lp1->l_text[doto])
  210.             *--cp2 = *--cp1;
  211.     }
  212.     for (i=0; i<n; ++i)            /* Add the characters    */
  213.         lp2->l_text[doto+i] = c;
  214.     wp = wheadp;                /* Update windows    */
  215.     while (wp != NULL) {
  216.         if (wp->w_linep == lp1)
  217.             wp->w_linep = lp2;
  218.         if (wp->w_dotp == lp1) {
  219.             wp->w_dotp = lp2;
  220.             if (wp==curwp || wp->w_doto>doto)
  221.                 wp->w_doto += n;
  222.         }
  223.         if (wp->w_markp == lp1) {
  224.             wp->w_markp = lp2;
  225.             if (wp->w_marko > doto)
  226.                 wp->w_marko += n;
  227.         }
  228.         wp = wp->w_wndp;
  229.     }
  230.     return (TRUE);
  231. }
  232.  
  233. /*
  234.  * Insert a newline into the buffer
  235.  * at the current location of dot in the current
  236.  * window. The funny ass-backwards way it does things
  237.  * is not a botch; it just makes the last line in
  238.  * the file not a special case. Return TRUE if everything
  239.  * works out and FALSE on error (memory allocation
  240.  * failure). The update of dot and mark is a bit
  241.  * easier then in the above case, because the split
  242.  * forces more updating.
  243.  */
  244. lnewline()
  245. {
  246.     register char    *cp1;
  247.     register char    *cp2;
  248.     register LINE    *lp1;
  249.     register LINE    *lp2;
  250.     register int    doto;
  251.     register WINDOW    *wp;
  252.  
  253.     lchange(WFHARD);
  254.     lp1  = curwp->w_dotp;            /* Get the address and    */
  255.     doto = curwp->w_doto;            /* offset of "."    */
  256.     if ((lp2=lalloc(doto)) == NULL)        /* New first half line    */
  257.         return (FALSE);
  258.     cp1 = &lp1->l_text[0];            /* Shuffle text around    */
  259.     cp2 = &lp2->l_text[0];
  260.     while (cp1 != &lp1->l_text[doto])
  261.         *cp2++ = *cp1++;
  262.     cp2 = &lp1->l_text[0];
  263.     while (cp1 != &lp1->l_text[lp1->l_used])
  264.         *cp2++ = *cp1++;
  265.     lp1->l_used -= doto;
  266.     lp2->l_bp = lp1->l_bp;
  267.     lp1->l_bp = lp2;
  268.     lp2->l_bp->l_fp = lp2;
  269.     lp2->l_fp = lp1;
  270.     wp = wheadp;                /* Windows        */
  271.     while (wp != NULL) {
  272.         if (wp->w_linep == lp1)
  273.             wp->w_linep = lp2;
  274.         if (wp->w_dotp == lp1) {
  275.             if (wp->w_doto < doto)
  276.                 wp->w_dotp = lp2;
  277.             else
  278.                 wp->w_doto -= doto;
  279.         }
  280.         if (wp->w_markp == lp1) {
  281.             if (wp->w_marko < doto)
  282.                 wp->w_markp = lp2;
  283.             else
  284.                 wp->w_marko -= doto;
  285.         }
  286.         wp = wp->w_wndp;
  287.     }    
  288.     return (TRUE);
  289. }
  290.  
  291. /*
  292.  * This function deletes "n" bytes,
  293.  * starting at dot. It understands how do deal
  294.  * with end of lines, etc. It returns TRUE if all
  295.  * of the characters were deleted, and FALSE if
  296.  * they were not (because dot ran into the end of
  297.  * the buffer. The "kflag" is TRUE if the text
  298.  * should be put in the kill buffer.
  299.  */
  300. ldelete(n, kflag)
  301. {
  302.     register char    *cp1;
  303.     register char    *cp2;
  304.     register LINE    *dotp;
  305.     register int    doto;
  306.     register int    chunk;
  307.     register WINDOW    *wp;
  308.  
  309.     while (n != 0) {
  310.         dotp = curwp->w_dotp;
  311.         doto = curwp->w_doto;
  312.         if (dotp == curbp->b_linep)    /* Hit end of buffer.    */
  313.             return (FALSE);
  314.         chunk = dotp->l_used-doto;    /* Size of chunk.    */
  315.         if (chunk > n)
  316.             chunk = n;
  317.         if (chunk == 0) {        /* End of line, merge.    */
  318.             lchange(WFHARD);
  319.             if (ldelnewline() == FALSE
  320.             || (kflag!=FALSE && kinsert('\n')==FALSE))
  321.                 return (FALSE);
  322.             --n;
  323.             continue;
  324.         }
  325.         lchange(WFEDIT);
  326.         cp1 = &dotp->l_text[doto];    /* Scrunch text.    */
  327.         cp2 = cp1 + chunk;
  328.         if (kflag != FALSE) {        /* Kill?        */
  329.             while (cp1 != cp2) {
  330.                 if (kinsert(*cp1) == FALSE)
  331.                     return (FALSE);
  332.                 ++cp1;
  333.             }
  334.             cp1 = &dotp->l_text[doto];
  335.         }
  336.         while (cp2 != &dotp->l_text[dotp->l_used])
  337.             *cp1++ = *cp2++;
  338.         dotp->l_used -= chunk;
  339.         wp = wheadp;            /* Fix windows        */
  340.         while (wp != NULL) {
  341.             if (wp->w_dotp==dotp && wp->w_doto>=doto) {
  342.                 wp->w_doto -= chunk;
  343.                 if (wp->w_doto < doto)
  344.                     wp->w_doto = doto;
  345.             }    
  346.             if (wp->w_markp==dotp && wp->w_marko>=doto) {
  347.                 wp->w_marko -= chunk;
  348.                 if (wp->w_marko < doto)
  349.                     wp->w_marko = doto;
  350.             }
  351.             wp = wp->w_wndp;
  352.         }
  353.         n -= chunk;
  354.     }
  355.     return (TRUE);
  356. }
  357.  
  358. /*
  359.  * Delete a newline. Join the current line
  360.  * with the next line. If the next line is the magic
  361.  * header line always return TRUE; merging the last line
  362.  * with the header line can be thought of as always being a
  363.  * successful operation, even if nothing is done, and this makes
  364.  * the kill buffer work "right". Easy cases can be done by
  365.  * shuffling data around. Hard cases require that lines be moved
  366.  * about in memory. Return FALSE on error and TRUE if all
  367.  * looks ok. Called by "ldelete" only.
  368.  */
  369. ldelnewline()
  370. {
  371.     register char    *cp1;
  372.     register char    *cp2;
  373.     register LINE    *lp1;
  374.     register LINE    *lp2;
  375.     register LINE    *lp3;
  376.     register WINDOW    *wp;
  377.  
  378.     lp1 = curwp->w_dotp;
  379.     lp2 = lp1->l_fp;
  380.     if (lp2 == curbp->b_linep) {        /* At the buffer end.    */
  381.         if (lp1->l_used == 0)        /* Blank line.        */
  382.             lfree(lp1);
  383.         return (TRUE);
  384.     }
  385.     if (lp2->l_used <= lp1->l_size-lp1->l_used) {
  386.         cp1 = &lp1->l_text[lp1->l_used];
  387.         cp2 = &lp2->l_text[0];
  388.         while (cp2 != &lp2->l_text[lp2->l_used])
  389.             *cp1++ = *cp2++;
  390.         wp = wheadp;
  391.         while (wp != NULL) {
  392.             if (wp->w_linep == lp2)
  393.                 wp->w_linep = lp1;
  394.             if (wp->w_dotp == lp2) {
  395.                 wp->w_dotp  = lp1;
  396.                 wp->w_doto += lp1->l_used;
  397.             }
  398.             if (wp->w_markp == lp2) {
  399.                 wp->w_markp  = lp1;
  400.                 wp->w_marko += lp1->l_used;
  401.             }
  402.             wp = wp->w_wndp;
  403.         }        
  404.         lp1->l_used += lp2->l_used;
  405.         lp1->l_fp = lp2->l_fp;
  406.         lp2->l_fp->l_bp = lp1;
  407.         free((char *) lp2);
  408.         return (TRUE);
  409.     }
  410.     if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
  411.         return (FALSE);
  412.     cp1 = &lp1->l_text[0];
  413.     cp2 = &lp3->l_text[0];
  414.     while (cp1 != &lp1->l_text[lp1->l_used])
  415.         *cp2++ = *cp1++;
  416.     cp1 = &lp2->l_text[0];
  417.     while (cp1 != &lp2->l_text[lp2->l_used])
  418.         *cp2++ = *cp1++;
  419.     lp1->l_bp->l_fp = lp3;
  420.     lp3->l_fp = lp2->l_fp;
  421.     lp2->l_fp->l_bp = lp3;
  422.     lp3->l_bp = lp1->l_bp;
  423.     wp = wheadp;
  424.     while (wp != NULL) {
  425.         if (wp->w_linep==lp1 || wp->w_linep==lp2)
  426.             wp->w_linep = lp3;
  427.         if (wp->w_dotp == lp1)
  428.             wp->w_dotp  = lp3;
  429.         else if (wp->w_dotp == lp2) {
  430.             wp->w_dotp  = lp3;
  431.             wp->w_doto += lp1->l_used;
  432.         }
  433.         if (wp->w_markp == lp1)
  434.             wp->w_markp  = lp3;
  435.         else if (wp->w_markp == lp2) {
  436.             wp->w_markp  = lp3;
  437.             wp->w_marko += lp1->l_used;
  438.         }
  439.         wp = wp->w_wndp;
  440.     }
  441.     free((char *) lp1);
  442.     free((char *) lp2);
  443.     return (TRUE);
  444. }
  445.  
  446. /*
  447.  * Replace plen characters before dot with argument string.
  448.  * Control-J characters in st are interpreted as newlines.
  449.  * There is a casehack disable flag (normally it likes to match
  450.  * case of replacement to what was there).
  451.  */
  452. lreplace(plen, st, f)
  453. register int    plen;            /* length to remove        */
  454. char        *st;            /* replacement string        */
  455. int        f;            /* case hack disable        */
  456. {
  457.     register int    rlen;        /* replacement length        */
  458.     register int    rtype;        /* capitalization         */
  459.     register int    c;        /* used for random characters    */
  460.     register int    doto;        /* offset into line        */
  461.  
  462.     /*
  463.      * Find the capitalization of the word that was found.
  464.      * f says use exact case of replacement string (same thing that
  465.      * happens with lowercase found), so bypass check.
  466.      */
  467.     backchar(TRUE, plen, KRANDOM);
  468.     rtype = _L;
  469.     c = lgetc(curwp->w_dotp, curwp->w_doto);
  470.     if (ISUPPER(c)!=FALSE  &&  f==FALSE) {
  471.         rtype = _U|_L;
  472.         if (curwp->w_doto+1 < llength(curwp->w_dotp)) {
  473.             c = lgetc(curwp->w_dotp, curwp->w_doto+1);
  474.             if (ISUPPER(c) != FALSE) {
  475.                 rtype = _U;
  476.             }
  477.         }
  478.     }
  479.  
  480.     /*
  481.      * make the string lengths match (either pad the line
  482.      * so that it will fit, or scrunch out the excess).
  483.      * be careful with dot's offset.
  484.      */
  485.     rlen = strlen(st);
  486.     doto = curwp->w_doto;
  487.     if (plen > rlen)
  488.         ldelete(plen-rlen, FALSE);
  489.     else if (plen < rlen) {
  490.         if (linsert(rlen-plen, ' ') == FALSE)
  491.             return (FALSE);
  492.     }
  493.     curwp->w_doto = doto;
  494.  
  495.     /*
  496.      * do the replacement:  If was capital, then place first 
  497.      * char as if upper, and subsequent chars as if lower.  
  498.      * If inserting upper, check replacement for case.
  499.      */
  500.     while ((c = *st++&0xff) != '\0') {
  501.         if ((rtype&_U)!=0  &&  ISLOWER(c)!=0)
  502.             c = TOUPPER(c);
  503.         if (rtype == (_U|_L))
  504.             rtype = _L;
  505.         if (c == '\n') {
  506.             if (curwp->w_doto == llength(curwp->w_dotp))
  507.                 forwchar(FALSE, 1, KRANDOM);
  508.             else {
  509.                 ldelete(1, FALSE);
  510.                 lnewline();
  511.             }
  512.         } else if (curwp->w_dotp == curbp->b_linep) {
  513.             linsert(1, c);
  514.         } else if (curwp->w_doto == llength(curwp->w_dotp)) {
  515.             ldelete(1, FALSE);
  516.             linsert(1, c);
  517.         } else
  518.             lputc(curwp->w_dotp, curwp->w_doto++, c);
  519.     }
  520.     lchange(WFHARD);
  521.     return (TRUE);
  522. }
  523.  
  524. /*
  525.  * Delete all of the text
  526.  * saved in the kill buffer. Called by commands
  527.  * when a new kill context is being created. The kill
  528.  * buffer array is released, just in case the buffer has
  529.  * grown to immense size. No errors.
  530.  */
  531. kdelete()
  532. {
  533.     if (kbufp != NULL) {
  534.         free((char *) kbufp);
  535.         kbufp = NULL;
  536.         kused = 0;
  537.         ksize = 0;
  538.     }
  539. }
  540.  
  541. /*
  542.  * Insert a character to the kill buffer,
  543.  * enlarging the buffer if there isn't any room. Always
  544.  * grow the buffer in chunks, on the assumption that if you
  545.  * put something in the kill buffer you are going to put
  546.  * more stuff there too later. Return TRUE if all is
  547.  * well, and FALSE on errors. Print a message on
  548.  * errors.
  549.  */
  550. kinsert(c)
  551. {
  552.     register char    *nbufp;
  553.     register int    i;
  554.  
  555.     if (kused == ksize) {
  556.         if ((nbufp=malloc(ksize+KBLOCK)) == NULL) {
  557.             eprintf("Too many kills");
  558.             return (FALSE);
  559.         }
  560.         for (i=0; i<ksize; ++i)
  561.             nbufp[i] = kbufp[i];
  562.         if (kbufp != NULL)
  563.             free((char *) kbufp);
  564.         kbufp  = nbufp;
  565.         ksize += KBLOCK;
  566.     }
  567.     kbufp[kused++] = c;
  568.     return (TRUE);
  569. }
  570.  
  571. /*
  572.  * This function gets characters from
  573.  * the kill buffer. If the character index "n" is
  574.  * off the end, it returns "-1". This lets the caller
  575.  * just scan along until it gets a "-1" back.
  576.  */
  577. kremove(n)
  578. {
  579.     if (n >= kused)
  580.         return (-1);
  581.     return (kbufp[n] & 0xFF);
  582. }
  583.